/**    
 * 文件名：AbstractAccessTokenManage.java</br>
 *    
 * 版本信息：v1.0</br>
 * 日期：2017年6月28日</br>
 * © 2005-2017 雷技信息科技（上海）有限公司版权所有</br>
 *
 */
package com.logic.landseaserver.common.token;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.logic.landseaserver.common.LandseaErrorMessageEnum;
import com.logic.landseaserver.common.exception.LandseaException;
import com.logic.landseaserver.domain.SystemAccessToken;

/**
 *
 * 项目名称：system-server</br>
 * 类名称：AbstractAccessTokenManage</br>
 * 类描述：调用第三方accessToken管理类,抽象的需要继承使用</br>
 * 创建人：Aaron</br>
 * 创建时间：2017年6月28日 下午3:54:39</br>
 * 
 * @version 1.0
 *
 */
public abstract class AbstractAccessTokenManage
{
    private static final Logger LOG = LoggerFactory.getLogger(AbstractAccessTokenManage.class);
    
    /**
     * 
     * [简要描述]：获取accessToken</br>
     * [详细描述]：过期会从新获取</br>
     * [作者]：Aaron(2017-06-28)</br>
     *
     * @param tokenId 主键
     * @param extendObject 扩展参数，自定义参数
     * @return SystemAccessToken
     * @throws LandseaException
     *
     */
    protected SystemAccessToken getAccessToken(String tokenId, Object extendObject)
        throws LandseaException
    {
        try
        {
            AccessTokenHelper accessTokenHelper = AccessTokenHelper.getInstance();
            
            LOG.info("getAccessToken|tokenId=" + tokenId);
            
            // 先获取本地缓存,能获取到继续下一步
            SystemAccessToken accessToken = accessTokenHelper.getFromCache(tokenId);
            if (null != accessToken)
            {
                LOG.info("getAccessToken|tokenId=" + tokenId + "|本地存在|" + accessToken.toString());
                accessToken = refreshAccessToken(tokenId, accessToken, accessTokenHelper, extendObject, true);
                return accessToken;
            }
            
            // 查询数据库
            accessToken = accessTokenHelper.getFromDb(tokenId);
            LOG.info("getAccessToken|tokenId=" + tokenId + "|查询数据库完毕|" + accessToken);
            
            if (null != accessToken)
            {
                accessToken = refreshAccessToken(tokenId, accessToken, accessTokenHelper, extendObject, false);
                return accessToken;
            }
            
            LOG.info("getAccessToken|tokenId=" + tokenId + "|数据库为空从第三方获取|");
            // 如果还为空，直接从第三方获取
            accessToken = getDestAccessToken(tokenId, extendObject);
            
            if (null == accessToken)
            {
                throw LandseaException.createException(LandseaErrorMessageEnum.COMMON_ACCESSTOKEN_ISNULL_ERROR);
            }
            
            // 新增入库，刷新本地缓存
            accessTokenHelper.add(tokenId, accessToken);
            LOG.info("getAccessToken|tokenId=" + tokenId + "|新增入库刷新缓存完毕|");
            
            return accessToken;
        }
        catch (LandseaException e)
        {
            throw e;
        }
        catch (Exception e)
        {
            LOG.error("getAccessToken error|tokenId=" + tokenId, e);
            throw LandseaException.createException(LandseaErrorMessageEnum.COMMON_GET_ACCESSTOKEN_ERROR, e);
        }
    }
    
    /**
     * 
     * [简要描述]：刷新</br>
     * [详细描述]：</br>
     * [作者]：Aaron(2017-06-28)</br>
     *
     * @param tokenId 主键
     * @param accessToken accessToken对象
     * @param accessTokenHelper
     * @param extendObject 扩展参数，自定义参数
     * @return SystemAccessToken
     * @throws LandseaException
     *
     */
    private SystemAccessToken refreshAccessToken(String tokenId, SystemAccessToken accessToken,
        AccessTokenHelper accessTokenHelper, Object extendObject, boolean isNeedQueryDb)
        throws LandseaException
    {
        // 查看是否过期
        if (accessTokenHelper.isTokenExpired(accessToken))
        {
            LOG.info("refreshAccessToken|token is expired| " + accessToken.toString());
            
            // 如果是查询数据库之后调用的该方法，则不需要继续查询了
            if (isNeedQueryDb)
            {
                // 过期了要先查询数据库（双机情况下要考虑此情况）
                accessToken = accessTokenHelper.getFromDb(tokenId);
                LOG.info("refreshAccessToken|本地过期,查询数据库| " + accessToken.toString());
                // 判断数据库中的是否过期
                if (!accessTokenHelper.isTokenExpired(accessToken))
                {
                    // 只更新本地缓存
                    accessTokenHelper.reset(tokenId, accessToken);
                    return accessToken;
                }
            }
            
            LOG.info("refreshAccessToken|数据库过期,需要从第三方获取");
            
            // 过期了就重新到第三方获取
            accessToken = getDestAccessToken(tokenId, extendObject);
            
            LOG.info("refreshAccessToken|tokenId=" + tokenId + "|从第三方获取结果|" + accessToken.toString());
            
            // 更新到数据库、刷新本地缓存
            accessTokenHelper.reset(tokenId, accessToken);
            LOG.info("refreshAccessToken|tokenId=" + tokenId + "|更新本地完毕|");
        }
        
        return accessToken;
    }
    
    /**
     * 
     * [简要描述]：获取第三方accessToken</br>
     * [详细描述]：核心请求第三方accessToken，调用者自己实现</br>
     * [作者]：Aaron(2017-06-28)</br>
     *
     * @param tokenId 数据库主键
     * @extendObject 扩展参数，自定义参数
     * @return SystemAccessToken
     * @throws LandseaException
     *
     */
    protected abstract SystemAccessToken getDestAccessToken(String tokenId, Object extendObject)
        throws LandseaException;
    
    /**
     * 
     * [简要描述]：获取tokenId</br>
     * [详细描述]：场景不同，规则不同，需要自己实现</br>
     * [作者]：Aaron(2017-06-28)</br>
     *
     * @param param 参数
     * @return TokenId
     *
     */
    protected abstract String getTokenId(Object param);
}
